package strx

import (
	"errors"
	"fmt"
	"sort"
	"strings"
	"unicode"
)

const (
	LF     = "\n"
	CR     = "\r"
	LFRune = '\n'
	CRRune = '\r'
)

// RemoveLineBreak Remove all line breaks
func RemoveLineBreak(s string) string {
	return ""
}

// TrimSpace trim left and right space
func TrimSpace(s string) string {
	if s == "" {
		return s
	}
	return TrimRightSpace(TrimLeftSpace(s))
}

// TrimRightSpace trim right space
func TrimRightSpace(s string) string {
	if s == "" {
		return s
	}

	if !unicode.IsSpace(LastRune(s)) {
		return s
	}

	return strings.TrimRightFunc(s, unicode.IsSpace)
}

// TrimLeftSpace trim left space
func TrimLeftSpace(s string) string {

	if s == "" {
		return s
	}

	if !unicode.IsSpace(FirstRune(s)) {
		return s
	}

	return strings.TrimLeftFunc(s, unicode.IsSpace)
}

// RemoveLeftLineBreak Removes the left line breaks
func RemoveLeftLineBreak(s string) string {

	if s == "" {
		return s
	}

	if !StartWith(s, CR) && !StartWith(s, LF) {
		return s
	}

	l := Len(s)
	i := l / 2

	left := Left(s, uint(i))
	for StartWith(left, CR) || StartWith(left, LF) {
		left = strings.TrimLeft(strings.TrimLeft(left, CR), LF)
		left = strings.TrimLeftFunc(left, unicode.IsSpace)
	}

	right := Right(s, uint(i))
	if EndWith(left, CR) || EndWith(left, LF) && StartWith(right, CR) || StartWith(right, LF) {
		right = RemoveLeftLineBreak(right)
	}
	return left + right
}

// IsBool tests whether the specified string is a bool value
func IsBool(s string) bool {
	if IsEmpty(s) {
		return false
	}
	ts := strings.TrimSpace(s)
	return ts == "true" || ts == "false"
}

// EndWith tests if a string ends with a specified suffix
func EndWith(s string, suffix string) bool {

	if suffix == "" {
		return true
	}
	return suffix == Right(s, uint(Len(s)-Len(suffix)))
}

// StartWith tests if a string starts with a specified prefix.
func StartWith(s string, prefix string) bool {

	if prefix == "" {
		return true
	}

	i := strings.Index(s, prefix)

	if i == -1 {
		return false
	}

	return i == 0
}

// RemoveFragment Removes characters in the specified index range from a string
func RemoveFragment(s string, sIndex uint, eIndex uint) (rs string, err error) {

	if sIndex > eIndex {
		err = errors.New("start index cannot be smaller than the end index")
		return
	}

	r := []rune(s)
	r = append(r[:sIndex], r[eIndex:]...)
	return string(r), nil
}

// RemoveMulti Removes the string with the specified index
func RemoveMulti(s string, multi ...int) string {
	if s == "" {
		return s
	}
	fmt.Println(s)

	sLen := Len(s)
	sort.Ints(multi)
	diff := multi[0] - sLen - 1

	valid := make([]int, 0)
	for _, v := range multi {
		if v < sLen-1 && v-sLen-1 > diff {
			diff = v - sLen - 1
		}

		if v < sLen-1 {
			valid = append(valid, v)
		}
	}

	r := []rune(s)
	for i, v := range valid {

		r = append(r[:v], r[v+1:]...)
		for y := i + 1; y < len(valid); y++ {
			valid[y] -= 1
		}

	}
	return string(r)
}

// RemoveOne Removes the string with the specified index
func RemoveOne(s string, i uint) string {
	if s == "" {
		return s
	}

	r := []rune(s)
	if i > uint(len(r)) {
		return s
	}
	return string(append(r[:i], r[i+1:]...))
}

// RemoveLast Removes the last occurrence of a specified string in a string
func RemoveLast(s string, remove string) string {
	if IsEmpty(s) {
		return ""
	}

	if remove == "" {
		return s
	}

	i := strings.LastIndex(s, remove)
	if i == -1 {
		return s
	}

	return Left(s, uint(i)) + Right(s, uint(i+Len(remove)))
}

// RemoveFirst Removes the first occurrence of a specified string in a string
func RemoveFirst(s string, remove string) string {

	if IsEmpty(s) {
		return ""
	}

	if remove == "" {
		return s
	}

	i := strings.Index(s, remove)
	if i == -1 {
		return s
	}
	return Left(s, uint(i)) + Right(s, uint(i+Len(remove)))
}

// Remove removes the specified string from the original string
func Remove(s string, remove string) string {
	if IsEmpty(s) {
		return ""
	}

	if remove == "" {
		return s
	}
	return strings.ReplaceAll(s, remove, "")
}

// Len Return string Rune length
func Len(s string) int {

	if len(s) == 0 {
		return 0
	}

	return len([]rune(s))
}

// IsNotBlank !IsBlank(s)
func IsNotBlank(s string) bool {
	return !IsBlank(s)
}

// IsBlank tests if the string is blank
// Includes Spaces and tabs
func IsBlank(s string) bool {

	if len(s) == 0 {
		return true
	}

	if strings.TrimSpace(s) == "" {
		return true
	}

	return false
}

// IsNotEmpty !IsEmpty(s)
func IsNotEmpty(s string) bool {
	return !IsEmpty(s)
}

// IsEmpty tests if a string is empty
func IsEmpty(s string) bool {
	if Len(s) == 0 {
		return true
	}

	return false
}

// Reverse Inverted string
func Reverse(s string) string {

	r := []rune(s)
	l := len(r)
	if l == 0 || l == 1 {
		return s
	}

	if l == 2 {
		return string(r[l-1]) + string(r[0])
	}

	if l == 3 {
		return string(r[l-1]) + string(r[l-2]) + string(r[0])
	}

	half := l / 2

	left := Left(s, uint(half))
	right := Right(s, uint(half))

	return Reverse(right) + Reverse(left)
}

// Left Gets the string to the left of the specified index
func Left(s string, i uint) string {
	r := []rune(s)
	if IsLonely(s) {
		return s
	}

	l := uint(len(r))

	if l < i {
		i = l
	}

	return string(r[:i])
}

// Right Gets the string to the right of the specified index
func Right(s string, i uint) string {
	r := []rune(s)
	if IsLonely(s) {
		return s
	}
	l := uint(len(r))

	if l < i {
		i = l
	}
	return string(r[i:])
}

// IsLonely Determine if the string is 0 or 1 character long
func IsLonely(s string) bool {

	if len(s) == 0 {
		return true
	}

	r := []rune(s)
	l := len(r)
	if l == 0 || l == 1 {
		return true
	}

	return false
}

// FragmentPreorder Pre-selects the string between the start index and the end index
func FragmentPreorder(s string, sIndex uint, eIndex uint) string {
	r := []rune(s)
	if IsLonely(s) {
		return s
	}
	l := uint(len(r))

	if sIndex > l {
		sIndex = l
	}

	if eIndex > l {
		eIndex = l
	}

	if sIndex == eIndex {
		return GetPreorder(s, sIndex)
	}
	return string(r[sIndex:eIndex])
}

// FragmentPostorder Post-selects the string between the start index and the end index
func FragmentPostorder(s string, sIndex uint, eIndex uint) string {
	r := []rune(s)
	if IsLonely(s) {
		return s
	}
	l := uint(len(r))

	if sIndex > l {
		sIndex = l
	}

	if eIndex > l {
		eIndex = l
	}

	sIndex = l - sIndex
	eIndex = l - eIndex

	if sIndex == eIndex {
		return GetPostorder(s, sIndex)
	}

	return string(r[eIndex:sIndex])
}

// First Returns the first character in a string
func First(s string) string {
	return GetPreorder(s, 0)
}

func FirstRune(s string) rune {
	return rune(First(s)[0])
}

// Last Returns the last character in a string
func Last(s string) string {
	return GetPostorder(s, 0)
}

func LastRune(s string) rune {
	return rune(Last(s)[0])
}

// GetPreorder Pre-select the character that specifies the index
func GetPreorder(s string, i uint) string {
	if len(s) == 0 {
		return ""
	}

	r := []rune(s)

	l := uint(len(r))
	if i > l {
		return ""
	}

	return string(r[i])
}

// GetPostorder Post-select the character that specifies the index
func GetPostorder(s string, i uint) string {
	if len(s) == 0 {
		return ""
	}

	r := []rune(s)

	l := uint(len(r))
	if i > l {
		return ""
	}
	return string(r[l-i-1])
}
